package com.wzw.expression.record.config;

import com.wzw.expression.expression.invocation.OperationInvocationCache;
import com.wzw.expression.expression.source.OperationSource;
import com.wzw.expression.record.annotations.EnableIRecord;
import com.wzw.expression.record.expression.IRecordAnnotationOperationProvider;
import com.wzw.expression.record.expression.IRecordInvocationContextProvider;
import com.wzw.expression.record.expression.IRecordInvocationMetadataProvider;
import com.wzw.expression.record.expression.IRecordsAnnotationOperationProvider;
import com.wzw.expression.record.proxy.BeanFactoryIRecordAdvisor;
import com.wzw.expression.record.proxy.IRecordMethodInterceptor;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.context.annotation.Role;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.NonNull;

import java.util.Objects;

/**
 * @author Wangzhiwen
 */
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class IRecordProxyConfiguration implements ImportAware {

    private AnnotationAttributes enableRecordAttributes;

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public IRecordMethodInterceptor iRecordMethodInterceptor(OperationSource operationSource, OperationInvocationCache operationInvocationCache) {
        return new IRecordMethodInterceptor(operationSource, operationInvocationCache);
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public BeanFactoryIRecordAdvisor beanFactoryRecordOperationSourceAdvisor
            (
                    OperationSource operationSource,
                    IRecordMethodInterceptor iRecordMethodInterceptor
            ) {
        BeanFactoryIRecordAdvisor advisor = new BeanFactoryIRecordAdvisor();
        advisor.setAdvice(iRecordMethodInterceptor);
        advisor.setExpressionOperationSource(operationSource);
        if (Objects.nonNull(enableRecordAttributes)) {
            advisor.setOrder(enableRecordAttributes.<Integer>getNumber("order"));
        }
        return advisor;
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public IRecordAnnotationOperationProvider iRecordExpressionOperationLoader() {
        return new IRecordAnnotationOperationProvider();
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public IRecordsAnnotationOperationProvider iRecordsAnnotationOperationProvider() {
        return new IRecordsAnnotationOperationProvider();
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public IRecordInvocationMetadataProvider iRecordInvocationMetadataProvider() {
        return new IRecordInvocationMetadataProvider();
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public IRecordInvocationContextProvider iRecordInvocationContextProvider() {
        return new IRecordInvocationContextProvider();
    }

    @Override
    public void setImportMetadata(@NonNull AnnotationMetadata importMetadata) {
        this.enableRecordAttributes = AnnotationAttributes.fromMap(
                importMetadata.getAnnotationAttributes(EnableIRecord.class.getName()));
        if (this.enableRecordAttributes == null) {
            throw new IllegalArgumentException("@EnableIRecord is not present on importing class " + importMetadata.getClassName());
        }
    }
}
